﻿namespace NorthernLights.Encryption.Symmetric
{
    using System;
    using System.IO;
    using System.Security.Cryptography;

    /// <summary>
    /// Symmetric Encryption library.
    /// </summary>
    public static class Encryption
    {
        /// <summary>
        /// Encrypt a message.
        /// </summary>
        /// <param name="dataToEncrypt">The message to encrypt.</param>
        /// <param name="password">The password to use.</param>
        /// <param name="salt">The salt to use.</param>
        /// <returns>The encrypted (base64) message or null.</returns>
        public static byte[] Encrypt(byte[] dataToEncrypt, byte[] password, byte[] salt)
        {
            if (dataToEncrypt == null || password == null || salt == null)
            {
                return null;
            }

            byte[] result = null;

            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, salt, 1000);

                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

                cryptoStream.Write(dataToEncrypt, 0, dataToEncrypt.Length);
                cryptoStream.FlushFinalBlock();

                result = memoryStream.ToArray();
            }
            catch (Exception ex)
            {
                DebugLogger.Instance.Write(ex);
            }
            finally
            {
                if (cryptoStream != null)
                {
                    try
                    {
                        cryptoStream.Close();
                    }
                    catch
                    {
                    }
                }

                if (memoryStream != null)
                {
                    memoryStream.Close();
                }

                if (aes != null)
                {
                    aes.Clear();
                }
            }

            return result;
        }

        /// <summary>
        /// Decrypt a message.
        /// </summary>
        /// <param name="dataToDecrypt">The encrypted (base64) message to decrypt.</param>
        /// <param name="password">The password to use.</param>
        /// <param name="salt">The salt to use.</param>
        /// <returns>The decrypted message or null.</returns>
        public static byte[] Decrypt(byte[] dataToDecrypt, byte[] password, byte[] salt)
        {
            if (dataToDecrypt == null || password == null || salt == null)
            {
                return null;
            }

            byte[] result = null;

            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, salt, 1000);

                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);

                byte[] data = dataToDecrypt;
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                byte[] decryptBytes = memoryStream.ToArray();
                result = decryptBytes;
            }
            catch (Exception ex)
            {
                DebugLogger.Instance.Write(ex);
            }
            finally
            {
                if (cryptoStream != null)
                {
                    try
                    {
                        cryptoStream.Close();
                    }
                    catch
                    {
                    }
                }

                if (memoryStream != null)
                {
                    memoryStream.Close();
                }

                if (aes != null)
                {
                    aes.Clear();
                }
            }

            return result;
        }
    }
}
